import abc
from itertools import combinations

from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.forms import formset_factory
from django.shortcuts import render, redirect
from django.http.response import HttpResponseBadRequest

from django.urls import reverse
from django.views import View

from algorithm.ahp import *
from algorithm.utils import map_to_matrix, point_to_float, get_model_structure, calc_weights
from .forms import *
from .models import *


# Create your views here.
class DataMetaView(LoginRequiredMixin, View, abc.ABC):

    def __init__(self):
        super().__init__()
        self.context = {'segment': ['data']}


def get_ahp(model_id):
    model_entry = Model.objects.get(pk=model_id)
    model = AHPModel(model_entry.context)
    return model, model_entry


def get_node_tuples(model_id):
    """根据模型，生成准则层的组合

    :param model_id: 模型id
    :return: 准则层（叶子节点）的组合，元组（父结点，子结点1，子结点2）的列表
    """
    model = get_ahp(model_id)[0]
    non_leaf = model.non_leaf
    node_tuples = []
    for node in non_leaf:
        comb = combinations(node.children, 2)
        node_tuples.extend([(node, *c) for c in comb])
    return node_tuples


def get_weight_formset(extra_length: int):
    """
    生成表单集工厂
    """
    return formset_factory(PreferenceForm, extra=extra_length)


def get_weight_question_list(weight_formset, node_tuples_name):
    weight_question_list = []
    for form, node_tuple in zip(weight_formset, node_tuples_name):
        weight_question_list.append({"form": form,
                                     "parent": node_tuple[0],
                                     "antecedent": node_tuple[1],
                                     "succedent": node_tuple[2]})
    return weight_question_list


class WeightCollectView(DataMetaView):

    def __init__(self):
        super(WeightCollectView, self).__init__()

    def get(self, request, model_id):
        self.context.update({'title': 'Weights',
                             'page_name': '权重收集',
                             'model_id': model_id})
        questionnaire_set = ParameterQuestionnaire.objects.filter(model_id=model_id)
        if not questionnaire_set.exists():
            # 没有生成问卷时，先生成并在数据库中保存
            node_tuples = get_node_tuples(model_id)[::-1]
            node_tuples_name = [[node_tuple[i].name for i in range(3)] for node_tuple in node_tuples]
            ParameterQuestionnaire.objects.create(questionnaire=node_tuples_name,
                                                  model_id=model_id)
        else:
            # 否则直接用库中的
            node_tuples_name = questionnaire_set[0].questionnaire
        ahp_model, model_entry = get_ahp(model_id)
        self.context["model"] = model_entry
        self.context.update(get_model_structure(model_entry, width="800px", height="500px"))
        self.context["nodes"] = {node.name: node.desc for node in ahp_model.all_nodes[::-1] if node.desc}
        self.context["formset"] = get_weight_formset(len(node_tuples_name))()
        self.context["question_list"] = get_weight_question_list(self.context["formset"], node_tuples_name)
        self.context["user_type"] = User.objects.get(pk=request.user.id).type
        return render(request, "data/weight_questionnaire.html", self.context)

    def post(self, request, model_id):
        node_tuples = get_node_tuples(model_id)
        weight_formset = get_weight_formset(len(node_tuples))(request.POST)

        if weight_formset.is_valid():
            result = []
            for node_pair, data in zip(node_tuples, weight_formset.cleaned_data):
                result.append({
                    "point": data["point"],
                    "parent_id": node_pair[0].id,
                    "antecedent_id": node_pair[1].id,
                    "succedent_id": node_pair[2].id,
                })
            # 存到参数答案库中
            pa = ParameterAnswer.objects.create(answer=result,
                                                user_id=request.user.id,
                                                param_questionnaire=ParameterQuestionnaire.objects.get(
                                                    model_id=model_id))
            triple_dict = {}
            for r in result:
                triple = [r["succedent_id"], r["antecedent_id"], point_to_float(r["point"])]
                if r["parent_id"] in triple_dict:
                    triple_dict[r["parent_id"]].append(triple)
                else:
                    triple_dict[r["parent_id"]] = [triple]
            for k, v in triple_dict.items():
                df = map_to_matrix(v)
                CR, weight = calc_weights(df).values()
                # 存到参数库中
                Parameter.objects.create(origin=df.to_dict(),
                                         weight=list(weight),
                                         cr=CR,
                                         node_id=k,
                                         user_id=request.user.id,
                                         param_answer=pa)
        return redirect(reverse('weight', args=(model_id,)))


def generate_answer_form(alternative_name_list):
    class AnswerForm(forms.Form):
        pass

    for an in alternative_name_list:
        AnswerForm.base_fields[an] = forms.DecimalField(max_value=10, min_value=0, max_digits=4, decimal_places=2,
                                                        widget=forms.NumberInput(
                                                            attrs={
                                                                "placeholder": "评分",
                                                                "class": "form-control"
                                                            }
                                                        ))
    return AnswerForm


class AnswerCollectView(DataMetaView):

    def __init__(self):
        super(AnswerCollectView, self).__init__()

    def get(self, request, instance_id, criterion_id):
        self.context.update({'title': 'Answer',
                             'page_name': '答案收集',
                             'instance_id': instance_id,
                             'criterion_id': criterion_id})
        questionnaire_set = Questionnaire.objects.filter(instance_id=instance_id)
        if not questionnaire_set.exists():
            return HttpResponseBadRequest("问卷无效！")
        else:
            questionnaire = questionnaire_set[0].questionnaire
        alternative_names = questionnaire.values()
        AnswerForm = generate_answer_form(alternative_names)
        model= get_ahp(Instance.objects.get(pk=instance_id).model_id)[0]
        leaves = {leaf.id: (leaf.name, leaf.desc) for leaf in model.leaves}
        self.context["context_name"] = leaves[criterion_id][0]
        self.context["criterion_desc"] = leaves[criterion_id][1]
        self.context["form"] = AnswerForm()
        self.context["user_type"] = User.objects.get(pk=request.user.id).type
        return render(request, "data/answer_questionnaire.html", self.context)

    def post(self, request, instance_id, criterion_id):
        questionnaire_entry = Questionnaire.objects.get(instance_id=instance_id)
        alternative_names = questionnaire_entry.questionnaire.values()
        AnswerForm = generate_answer_form(alternative_names)

        answer_form = AnswerForm(request.POST)
        if answer_form.is_valid():
            name2id = {v: k for k, v in questionnaire_entry.questionnaire.items()}
            Answer.objects.create(answer={name2id[k]: float(v) for k, v in answer_form.cleaned_data.items()},
                                  user_id=request.user.id,
                                  criterion_id=criterion_id,
                                  questionnaire=questionnaire_entry
                                  )
            return redirect(reverse('criteria_list', args=(instance_id,)))


@login_required
def get_criteria_list_view(request, instance_id):
    instance = Instance.objects.get(pk=instance_id)
    model_id = Model.objects.get(instance=instance).id
    model = get_ahp(model_id)[0]
    context = {'title': 'Answer',
               'segment': ['data'],
               'page_name': '问卷列表',
               "instance": instance,
               "user_type": User.objects.get(pk=request.user.id).type}
    if model_id == 2:
        context["leaves1"] = [{"id": leaf.id, "name": leaf.name,
                              "answered": Answer.objects.filter(user=request.user, criterion_id=leaf.id).exists()}
                              for leaf in model.leaves if leaf.area == 1]
        context["leaves2"] = [{"id": leaf.id, "name": leaf.name,
                              "answered": Answer.objects.filter(user=request.user, criterion_id=leaf.id).exists()}
                              for leaf in model.leaves if leaf.area == 2]
        context["leaves3"] = [{"id": leaf.id, "name": leaf.name,
                              "answered": Answer.objects.filter(user=request.user, criterion_id=leaf.id).exists()}
                              for leaf in model.leaves if leaf.area == 3]
        return render(request, "data/criteria_list_test.html", context)
    else:
        context["leaves"] = [{"id": leaf.id, "name": leaf.name,
                             "answered": Answer.objects.filter(user=request.user, criterion_id=leaf.id).exists()}
                             for leaf in model.leaves]
        return render(request, "data/criteria_list.html", context)


@login_required
def data_list_view(request):
    context = {'title': 'Data Management', 'segment': ['data'], 'page_name': '数据管理',
               'answer':
                   Answer.objects.filter(questionnaire__instance__of_project__owner=request.user)
                       .order_by('create_datetime', 'validity'),
               'param_answer':
                   ParameterAnswer.objects.filter(param_questionnaire__model__creator=request.user)
                       .order_by('create_datetime', 'validity'),
               'parameter':
                   Parameter.objects.filter(param_answer__param_questionnaire__model__creator=request.user),
               }
    return render(request, 'data/data_list.html', context)


@login_required
def answer_list_view(request):
    context = {'title': 'Answer Management', 'segment': ['data', 'answer'], 'page_name': '被试答案管理',
               'answer':
                   Answer.objects.filter(questionnaire__instance__of_project__owner=request.user)
                       .order_by('create_datetime', 'validity'),
               }
    return render(request, 'data/data_list.html', context)


@login_required
def param_answer_list_view(request):
    context = {'title': 'Parameter Answer Management', 'segment': ['data', 'param_answer'], 'page_name': '专家答案管理',
               'param_answer':
                   ParameterAnswer.objects.filter(param_questionnaire__model__creator=request.user)
                       .order_by('create_datetime', 'validity'),
               }
    return render(request, 'data/data_list.html', context)


@login_required
def param_list_view(request):
    context = {'title': 'Parameter Management', 'segment': ['data', 'parameter'], 'page_name': '参数管理',
               'parameter':
                   Parameter.objects.filter(param_answer__param_questionnaire__model__creator=request.user),
               }
    return render(request, 'data/data_list.html', context)


@login_required
def reverse_validity(request, data_type, entry_id, old_status):
    if old_status == 'True':
        new_status = False
    elif old_status == 'False':
        new_status = True
    else:
        return HttpResponseBadRequest("状态错误")

    dt_dict = {'param_answer': ParameterAnswer,
               'answer': Answer}
    AnswerModel = dt_dict.get(data_type)
    if AnswerModel is not None:
        AnswerModel.objects.filter(pk=entry_id).update(validity=new_status)
        return redirect(request.environ["HTTP_REFERER"])
    else:
        return HttpResponseBadRequest("数据类型错误")


@login_required
def data_index_view(request):
    context = {'title': 'Data Management', 'segment': ['data'], 'page_name': '数据管理',
               'param_answer_count': ParameterAnswer.objects.filter(param_questionnaire__model__creator=request.user)
                   .count(),
               'answer_count': Answer.objects.filter(questionnaire__instance__of_project__owner=request.user).count(),
               'param_count': Parameter.objects.filter(param_answer__param_questionnaire__model__creator=request.user)
                   .count(), }

    return render(request, 'data/index.html', context)


@login_required
def create_questionnaire(request, instance_id):
    alternative_queryset = Alternative.objects.filter(instancehasalternative__instance_id=instance_id)
    questionnaire = {a.id: a.name for a in alternative_queryset}
    Questionnaire.objects.create(questionnaire=questionnaire,
                                 instance_id=instance_id)
    return redirect(reverse('instance_detail', args=(instance_id,)))


@login_required
def answer_detail_view(request, answer_id):
    answer = Answer.objects.get(pk=answer_id)
    context = {'title': 'Data Management', 'segment': ['data'], 'page_name': '被试答案详情',
               'answer': answer,
               }
    pass


@login_required
def param_answer_detail_view(request, param_answer_id):
    param_answer = ParameterAnswer.objects.get(pk=param_answer_id)
    context = {'title': 'Data Management', 'segment': ['data'], 'page_name': '专家答案详情',
               'answer': param_answer,
               }
    pass


@login_required
def param_detail_view(request, param_id):
    param = ParameterAnswer.objects.get(pk=param_id)
    context = {'title': 'Data Management', 'segment': ['data'], 'page_name': '参数详情',
               'answer': param,
               }
    pass
